#include "StdAfx.h"

#include "3dEngine.h"
#include "RoadRenderNode.h"
#include "CullBuffer.h"
#include "terrain.h"
#include "ObjMan.h"
#include "MeshCompiler/MeshCompiler.h"
#include "VoxTerrain.h"

const float fRoadAreaZRange = 2.5f;
const float fRoadTerrainZOffset = 0.06f;

// tmp buffers used during road mesh creation
PodArray<SVF_P3F_C4B_T2S> CRoadRenderNode::m_lstVerticesMerged;
PodArray<uint16> CRoadRenderNode::m_lstIndicesMerged;
PodArray<SPipTangents> CRoadRenderNode::m_lstTangMerged;
PodArray<Vec3> CRoadRenderNode::m_lstVerts;
PodArray<uint16> CRoadRenderNode::m_lstIndices;
PodArray<SPipTangents> CRoadRenderNode::m_lstTang;
PodArray<SVF_P3F_C4B_T2S> CRoadRenderNode::m_lstVertices;
CPolygonClipContext CRoadRenderNode::s_tmpClipContext;

CRoadRenderNode::CRoadRenderNode()
{
	m_pRenderMesh = NULL;
	m_pMaterial = NULL;
	m_arrTexCoors[0] = m_arrTexCoorsGlobal[0] = 0;
	m_arrTexCoors[1] = m_arrTexCoorsGlobal[1] = 1;
#if ROAD_PHYSICS 
  m_pPhysEnt = NULL;
  m_pPhysGeom = NULL;
#endif
	m_sortPrio = 0;
	m_nLayerId = 0;
	m_bIgnoreTerrainHoles = false;

	GetInstCount(eERType_Road)++;
}

CRoadRenderNode::~CRoadRenderNode()
{
  DePhysicalize();
	Get3DEngine()->UnRegisterEntity(this);
	GetRenderer()->DeleteRenderMesh(m_pRenderMesh);
  Get3DEngine()->FreeRenderNodeState(this);

  Get3DEngine()->m_lstRoadRenderNodesForUpdate.Delete(this);

	GetInstCount(eERType_Road)--;
}

void CRoadRenderNode::SetVertices( const Vec3 * pVertsAll, int nVertsNumAll, 
                                  float fTexCoordBegin, float fTexCoordEnd, 
                                  float fTexCoordBeginGlobal, float fTexCoordEndGlobal )
{
  if(pVertsAll != m_arrVerts.GetElements())
  {
    m_arrVerts.Clear();
    m_arrVerts.AddList((Vec3*)pVertsAll, nVertsNumAll);

		// work around for cracks between road segments
		if(m_arrVerts.Count()>=4)
		{
			if(fTexCoordBegin != fTexCoordBeginGlobal)
			{
				Vec3 m0 = (m_arrVerts[0]+m_arrVerts[1])*.5f;
				Vec3 m1 = (m_arrVerts[2]+m_arrVerts[3])*.5f;
				Vec3 vDir = (m0-m1).GetNormalized()*0.01f;
				m_arrVerts[0] += vDir;
				m_arrVerts[1] += vDir;
			}

			if(fTexCoordEnd != fTexCoordEndGlobal)
			{
				int n = m_arrVerts.Count();
				Vec3 m0 = (m_arrVerts[n-1]+m_arrVerts[n-2])*.5f;
				Vec3 m1 = (m_arrVerts[n-3]+m_arrVerts[n-4])*.5f;
				Vec3 vDir = (m0-m1).GetNormalized()*0.01f;
				m_arrVerts[n-1] += vDir;
				m_arrVerts[n-2] += vDir;
			}
		}
  }

  // Adjust uv coords to smaller range to avoid f16 precision errors.
	// Need to adjust global ranges too to keep relative fades at ends of spline
	const float fNodeStart = static_cast<float>(static_cast<int>(fTexCoordBegin));
	const float fNodeOffset = fTexCoordBegin - fNodeStart;

  m_arrTexCoors[0] = fNodeOffset;
  m_arrTexCoors[1] = fNodeOffset + (fTexCoordEnd - fTexCoordBegin);

  m_arrTexCoorsGlobal[0] = fTexCoordBeginGlobal - fNodeStart;
  m_arrTexCoorsGlobal[1] = fTexCoordBeginGlobal + (fTexCoordEndGlobal - fTexCoordBeginGlobal) - fNodeStart;

  m_WSBBox.Reset();
  for(int i=0; i<nVertsNumAll; i++)
    m_WSBBox.Add(m_arrVerts[i]);

  m_WSBBox.min -= Vec3(0.1f,0.1f,fRoadAreaZRange);
  m_WSBBox.max += Vec3(0.1f,0.1f,fRoadAreaZRange);

  if(GetCVars()->e_VoxTer && GetIntegrationType())
  {
    Compile();
  }
  else
  {
    ScheduleRebuild();
  }
}

void CRoadRenderNode::Compile() PREFAST_SUPPRESS_WARNING(6262) //function uses > 32k stack space
{
  LOADING_TIME_PROFILE_SECTION;

  int nVertsNumAll = m_arrVerts.Count();

	assert(!(nVertsNumAll&1));

	if(nVertsNumAll<4)
		return;

	// free old object and mesh
	if(m_pRenderMesh)
		GetRenderer()->DeleteRenderMesh(m_pRenderMesh);
	m_pRenderMesh = NULL;

  bool bCheckVoxels = false;

  Plane arrPlanes[6];
	float arrTexCoors[2];

	// update object bbox
	if(!(GetCVars()->e_VoxTer && GetIntegrationType()))
	{
		Vec3 segmentOffset(0, 0, 0);
		if (Get3DEngine()->m_pSegmentsManager)
		{
			segmentOffset = GetTerrain()->GetSegmentOrigin(m_nSID);
		}

		m_WSBBox.Reset();
		for(int i=0; i<nVertsNumAll; i++)
		{
			Vec3 vTmp(m_arrVerts[i].x,m_arrVerts[i].y,Get3DEngine()->GetTerrainElevation(m_arrVerts[i].x,m_arrVerts[i].y,true, m_nSID)+fRoadTerrainZOffset);
			m_WSBBox.Add(vTmp);
		}

		// prepare arrays for final mesh
		const int nMaxVerticesToMerge = 1024*10; // limit memory usage
		m_lstVerticesMerged.PreAllocate(nMaxVerticesToMerge, 0);
		m_lstIndicesMerged.PreAllocate(nMaxVerticesToMerge*6, 0);
		m_lstTangMerged.PreAllocate(nMaxVerticesToMerge, 0);

		float fChunksNum = (float)((nVertsNumAll-2)/2);
		float fTexStep = (m_arrTexCoors[1] - m_arrTexCoors[0])/fChunksNum;

		// for every trapezoid
		for(int nVertId=0; nVertId<=nVertsNumAll-4; nVertId+=2)
		{
			const Vec3 * pVerts = &m_arrVerts[nVertId];

			if(	pVerts[0] == pVerts[1] ||
					pVerts[1] == pVerts[2] ||
					pVerts[2] == pVerts[3] ||
					pVerts[3] == pVerts[0])
				continue;

			// get texture coordinates range
			arrTexCoors[0] = m_arrTexCoors[0]+fTexStep*(nVertId/2);
			arrTexCoors[1] = m_arrTexCoors[0]+fTexStep*(nVertId/2+1);

			GetClipPlanes(&arrPlanes[0], 4, nVertId);

			// make trapezoid 2d bbox
			AABB WSBBox; 
			WSBBox.Reset();
			for(int i=0; i<4; i++)
			{
				Vec3 vTmp(pVerts[i].x,pVerts[i].y,pVerts[i].z);
				WSBBox.Add(vTmp);
			}

			// make vert array
			// handle points with negative values
			int nUnitSize = GetTerrain()->GetHeightMapUnitSize();
			int x1 = int(WSBBox.min.x + segmentOffset.x)/nUnitSize*nUnitSize;
			int x2 = int(WSBBox.max.x + segmentOffset.x)/nUnitSize*nUnitSize+nUnitSize;
			int y1 = int(WSBBox.min.y + segmentOffset.y)/nUnitSize*nUnitSize;
			int y2 = int(WSBBox.max.y + segmentOffset.y)/nUnitSize*nUnitSize+nUnitSize;
			x1 -= (int)segmentOffset.x;
			x2 -= (int)segmentOffset.x;
			y1 -= (int)segmentOffset.y;
			y2 -= (int)segmentOffset.y;

			// make arrays of verts and indices used in trapezoid area
			m_lstVerts.Clear();
			m_lstIndices.Clear();

			for(int x = x1; x <= x2; x += nUnitSize)
			for(int y = y1; y <= y2; y += nUnitSize)
			{ 
				Vec3 vTmp((float)x,(float)y,GetTerrain()->GetZ(x,y,m_nSID, true));

				if(GetCVars()->e_VoxTer && !GetCVars()->e_VoxTerHeightmapEditing)
				{
					Vec3 vTestPos( (float)x,(float)y, pVerts[0].z+fRoadAreaZRange );
					Vec3 vNormal = Get3DEngine()->GetTerrainSurfaceNormal(vTestPos);
					vTmp += vNormal*0.125f;
				}
				m_lstVerts.Add(vTmp);
			}
			// make indices
			int dx = (x2-x1)/nUnitSize;
			int dy = (y2-y1)/nUnitSize;

			for(int x = 0; x < dx; x ++)
			{
				for(int y = 0; y < dy; y ++)
				{
					int nIdx0 = (x*(dy+1) + y);
					int nIdx1 = (x*(dy+1) + y+(dy+1));
					int nIdx2 = (x*(dy+1) + y+1);
					int nIdx3 = (x*(dy+1) + y+1+(dy+1));

					assert(nIdx3 < m_lstVerts.Count());

					int X_in_meters = x1+x*nUnitSize;
					int Y_in_meters = y1+y*nUnitSize;

					CTerrain* pTerrain = GetTerrain();
					
					if(m_bIgnoreTerrainHoles || (pTerrain && !pTerrain->GetHole(X_in_meters, Y_in_meters, 0)))
					{
						if (pTerrain && pTerrain->IsMeshQuadFlipped(X_in_meters, Y_in_meters, nUnitSize, 0))
						{
							m_lstIndices.Add(nIdx0);
							m_lstIndices.Add(nIdx1);
							m_lstIndices.Add(nIdx3);

							m_lstIndices.Add(nIdx0);
							m_lstIndices.Add(nIdx3);
							m_lstIndices.Add(nIdx2);
						}
						else
						{
							m_lstIndices.Add(nIdx0);
							m_lstIndices.Add(nIdx1);
							m_lstIndices.Add(nIdx2);

							m_lstIndices.Add(nIdx1);
							m_lstIndices.Add(nIdx3);
							m_lstIndices.Add(nIdx2);
						}
					}
				}
			}

			// clip triangles
			int nOrigCount = m_lstIndices.Count();
			for(int i=0; i<nOrigCount; i+=3)
			{
				if(ClipTriangle(m_lstVerts, m_lstIndices, i, arrPlanes))
				{
					i-=3;
					nOrigCount-=3;
				}
			}

			if(m_lstIndices.Count()<3 || m_lstVerts.Count()<3)
				continue;

			// allocate tangent array
			m_lstTang.Clear();
			m_lstTang.PreAllocate(m_lstVerts.Count(),m_lstVerts.Count());

			int nStep = CTerrain::GetHeightMapUnitSize();

			Vec3 vWSBoxCenter = m_WSBBox.GetCenter(); //vWSBoxCenter.z=0;

			// make real vertex data
			m_lstVertices.Clear();
			for(int i=0; i<m_lstVerts.Count(); i++)
			{
				SVF_P3F_C4B_T2S tmp;
      
				Vec3 vWSPos = m_lstVerts[i];
      
				tmp.xyz = (m_lstVerts[i] - vWSBoxCenter);

				// do texgen
				float d0 = arrPlanes[0].DistFromPlane(vWSPos);
				float d1 = arrPlanes[1].DistFromPlane(vWSPos);
				float d2 = arrPlanes[2].DistFromPlane(vWSPos);
				float d3 = arrPlanes[3].DistFromPlane(vWSPos);

				float t = fabsf(d0+d1) < FLT_EPSILON ? 0.0f : d0/(d0+d1);
				tmp.st = Vec2f16((1-t)*fabs(arrTexCoors[0]) + t*fabs(arrTexCoors[1]), fabsf(d2+d3) < FLT_EPSILON ? 0.0f : d2/(d2+d3));

				// calculate alpha value
				float fAlpha = 1.f;
				if(fabs(arrTexCoors[0]-m_arrTexCoorsGlobal[0])<0.01f)
					fAlpha = CLAMP(t,0,1.f);
				else if(fabs(arrTexCoors[1]-m_arrTexCoorsGlobal[1])<0.01f)
					fAlpha = CLAMP(1.f-t,0,1.f);

				tmp.color.bcolor[0] = 255;
				tmp.color.bcolor[1] = 255;
				tmp.color.bcolor[2] = 255;
				tmp.color.bcolor[3] = uint8(255.f*fAlpha);
				SwapEndian(tmp.color.dcolor, eLittleEndian);

				m_lstVertices.Add(tmp);

				Vec3 vNormal = GetTerrain()->GetTerrainSurfaceNormal(vWSPos, 0.25f, bCheckVoxels, m_nSID);

				Vec3 vBinorm = pVerts[1]-pVerts[0]; 
				vBinorm.Normalize();

				Vec3 vTang = pVerts[2]-pVerts[0]; 
				vTang.Normalize();

				vBinorm = -vNormal.Cross(vTang);
				vTang = vNormal.Cross(vBinorm);

				m_lstTang[i].Binormal = Vec4sf(tPackF2B(vBinorm.x),tPackF2B(vBinorm.y),tPackF2B(vBinorm.z), tPackF2B(-1));
				m_lstTang[i].Tangent  = Vec4sf(tPackF2B(vTang.x),tPackF2B(vTang.y),tPackF2B(vTang.z), tPackF2B(-1));
			}

			// shift indices
			for(int i=0; i<m_lstIndices.Count(); i++)
				m_lstIndices[i] += m_lstVerticesMerged.Count();

			if(m_lstVerticesMerged.Count() + m_lstVertices.Count() > nMaxVerticesToMerge)
			{
				Warning( "Road object is too big, has to be split into several smaller parts (pos=%d,%d,%d)", (int)m_WSBBox.GetCenter().x, (int)m_WSBBox.GetCenter().y, (int)m_WSBBox.GetCenter().z );
				Get3DEngine()->UnRegisterEntity(this);
				return;
			}

			m_lstIndicesMerged.AddList(m_lstIndices);
			m_lstVerticesMerged.AddList(m_lstVertices);
			m_lstTangMerged.AddList(m_lstTang);
		}

		mesh_compiler::CMeshCompiler meshCompiler;
		meshCompiler.WeldPos_VF_P3X( m_lstVerticesMerged, m_lstTangMerged, m_lstIndicesMerged, VEC_EPSILON, GetBBox() );

		DePhysicalize();

		// make render mesh
		if(m_lstIndicesMerged.Count())
		{
			m_pRenderMesh = GetRenderer()->CreateRenderMeshInitialized(
				m_lstVerticesMerged.GetElements(), m_lstVerticesMerged.Count(), eVF_P3F_C4B_T2S, 
				m_lstIndicesMerged.GetElements(), m_lstIndicesMerged.Count(), prtTriangleList,
				"RoadRenderNode",GetName(), eRMT_Static, 1, 0, NULL, NULL, false, true, m_lstTangMerged.GetElements());

			float texelAreaDensity = 1.0f;
			{
				const size_t indexCount = m_lstIndicesMerged.size();
				const size_t vertexCount = m_lstVerticesMerged.size();

				if ((indexCount > 0) && (vertexCount > 0))
				{
					float posArea;
					float texArea;
					const char* errorText = "";

					const bool ok = CMeshHelpers::ComputeTexMappingAreas(
						indexCount, &m_lstIndicesMerged[0],
						vertexCount, 
						&m_lstVerticesMerged[0].xyz, sizeof(m_lstVerticesMerged[0]),
						&m_lstVerticesMerged[0].st, sizeof(m_lstVerticesMerged[0]),
						posArea, texArea, errorText);

					if (ok)
					{
						texelAreaDensity = texArea / posArea;
					}
					else
					{
						gEnv->pLog->LogError("Failed to compute texture mapping density for mesh '%s': %s", GetName(), errorText);
					}
				}
			}

			m_pRenderMesh->SetChunk((m_pMaterial!=NULL) ? (IMaterial*)m_pMaterial : GetMatMan()->GetDefaultMaterial(),
				0,m_lstVerticesMerged.Count(), 0,m_lstIndicesMerged.Count(), texelAreaDensity);
			m_WSBBox.Move(segmentOffset);
			Vec3 vWSBoxCenter = m_WSBBox.GetCenter(); //vWSBoxCenter.z=0;
			AABB OSBBox(m_WSBBox.min - vWSBoxCenter,m_WSBBox.max - vWSBoxCenter);
			m_pRenderMesh->SetBBox(OSBBox.min, OSBBox.max);
		}

#if ROAD_PHYSICS 
		// physicalize every trapezoid
		/*for(int nVertId=0; nVertId<=nVertsNumAll-4; nVertId+=2)
		{
			const Vec3 * pVerts = &pVertsAll[nVertId];

			static PodArray<Vec3> lstVerts; lstVerts.Clear();
			static uint16 g_idxBrd[] = { 0,1,2, 3,2,1, 4,6,5, 7,5,6, 0,2,6, 0,6,4, 3,1,5, 3,5,7 };
			lstVerts.PreAllocate(8,8);
			for(int i=0;i<8;i++)
			(lstVerts[i] = pVerts[i&3]).z += 0.3f*(1-(i>>1&2));
			if(m_pMaterial)
				Physicalize(g_idxBrd,sizeof(g_idxBrd)/sizeof(g_idxBrd[0]), lstVerts.GetElements(), m_pMaterial->GetSurfaceTypeId());
		}*/
#endif
  }
	else
  {
    float fChunksNum = (float)((nVertsNumAll-2)/2);
    float fTexStep = (m_arrTexCoors[1] - m_arrTexCoors[0])/fChunksNum;

    // for every trapezoid
    for(int nVertId=0; nVertId<=nVertsNumAll-4; nVertId+=2)
    {
      const Vec3 * pVerts = &m_arrVerts[nVertId];

      if(	pVerts[0] == pVerts[1] ||
        pVerts[1] == pVerts[2] ||
        pVerts[2] == pVerts[3] ||
        pVerts[3] == pVerts[0])
        continue;

      // get texture coordinates range
      arrTexCoors[0] = m_arrTexCoors[0]+fTexStep*(nVertId/2);
      arrTexCoors[1] = m_arrTexCoors[0]+fTexStep*(nVertId/2+1);

      // define 6 clip planes
      arrPlanes[0].SetPlane(pVerts[0],pVerts[1],pVerts[1]+Vec3(0,0,1));
      arrPlanes[1].SetPlane(pVerts[2],pVerts[3],pVerts[3]+Vec3(0,0,-1));
      arrPlanes[2].SetPlane(pVerts[0],pVerts[2],pVerts[2]+Vec3(0,0,-1));
      arrPlanes[3].SetPlane(pVerts[1],pVerts[3],pVerts[3]+Vec3(0,0,1));

      Vec3 vHeight(0,0,fRoadAreaZRange);
      arrPlanes[4].SetPlane(pVerts[0]-vHeight,pVerts[1]-vHeight,pVerts[2]-vHeight);
      arrPlanes[5].SetPlane(pVerts[1]+vHeight,pVerts[0]+vHeight,pVerts[2]+vHeight);
    }
  }

	// activate rendering
	Get3DEngine()->RegisterEntity(this);
}

void CRoadRenderNode::SetSortPriority(uint8 sortPrio)
{
	m_sortPrio = sortPrio;
}

void CRoadRenderNode::SetIgnoreTerrainHoles(bool bVal)
{
	m_bIgnoreTerrainHoles = bVal;
}

void CRoadRenderNode::Render(const SRendParams &_RendParams)
{
  FUNCTION_PROFILER_3DENGINE;

	if(!GetCVars()->e_Roads)
		return; // false;

	if(GetCVars()->e_Roads==2 && _RendParams.fDistance<2.f)
		ScheduleRebuild();

  if( GetIntegrationType() && GetCVars()->e_VoxTerHideIntegrated && GetCVars()->e_VoxTer )
    return; // baked into terrain

	SRendParams RendParams(_RendParams);

	CRenderObject * pObj = GetIdentityCRenderObject();
  if (!pObj)
    return; // false;

	pObj->m_DynLMMask[m_nRenderThreadListID] = RendParams.nDLightMask;
	pObj->m_ObjFlags |= RendParams.dwFObjFlags;
	pObj->m_II.m_AmbColor = RendParams.AmbientColor;
  Vec3 vWSBoxCenter = m_WSBBox.GetCenter(); vWSBoxCenter.z += 0.01f;
  pObj->m_II.m_Matrix.SetTranslation(vWSBoxCenter);

  RendParams.nRenderList = EFSLIST_DECAL;

	RendParams.m_ShadowMapCasters = 0;

	pObj->m_nSort =  m_sortPrio;

//	if (RendParams.pShadowMapCasters)
	pObj->m_ObjFlags |= (FOB_DECAL | FOB_INSHADOW | FOB_NO_FOG | FOB_TRANS_TRANSLATE);

	if(_RendParams.pTerrainTexInfo && (_RendParams.dwFObjFlags & (FOB_BLEND_WITH_TERRAIN_COLOR/* | FOB_AMBIENT_OCCLUSION*/)))
	{
		pObj->m_nTextureID = _RendParams.pTerrainTexInfo->nTex0;
		//pObj->m_nTextureID1 = _RendParams.pTerrainTexInfo->nTex1;
    SRenderObjData *pOD = GetRenderer()->EF_GetObjData(pObj, true);
		pOD->m_fTempVars[0] = _RendParams.pTerrainTexInfo->fTexOffsetX;
		pOD->m_fTempVars[1] = _RendParams.pTerrainTexInfo->fTexOffsetY;
		pOD->m_fTempVars[2] = _RendParams.pTerrainTexInfo->fTexScale;
		pOD->m_fTempVars[3] = _RendParams.pTerrainTexInfo->fTerrainMinZ;
		pOD->m_fTempVars[4] = _RendParams.pTerrainTexInfo->fTerrainMaxZ;
	}

	if(m_pRenderMesh)
		m_pRenderMesh->Render(RendParams, pObj, 
			(m_pMaterial!=NULL) ? (IMaterial*)m_pMaterial : GetMatMan()->GetDefaultMaterial());

//	return (m_pRenderMesh != NULL);
}

bool CRoadRenderNode::ClipTriangle(PodArray<Vec3> & lstVerts, PodArray<uint16> & lstInds, int nStartIdxId, Plane * pPlanes)
{
	const PodArray<Vec3>& clipped = s_tmpClipContext.Clip(
		lstVerts[lstInds[nStartIdxId+0]],
		lstVerts[lstInds[nStartIdxId+1]],
		lstVerts[lstInds[nStartIdxId+2]],
		pPlanes, 4);

	if(clipped.Count() < 3)
	{
		lstInds.Delete(nStartIdxId, 3);
		return true; // entire triangle is clipped away
	}

	if(clipped.Count() == 3)
		if(clipped[0].IsEquivalent(lstVerts[lstInds[nStartIdxId+0]]))
			if(clipped[1].IsEquivalent(lstVerts[lstInds[nStartIdxId+1]]))
				if(clipped[2].IsEquivalent(lstVerts[lstInds[nStartIdxId+2]]))
					return false; // entire triangle is in

	// replace old triangle with several new triangles
	int nStartId = lstVerts.Count();
	lstVerts.AddList(clipped);

	// put first new triangle into position of original one
	lstInds[nStartIdxId+0] = nStartId+0;
	lstInds[nStartIdxId+1] = nStartId+1;
	lstInds[nStartIdxId+2] = nStartId+2;

	// put others in the end
	for(int i=1; i<clipped.Count()-2 ; i++)
	{
		lstInds.Add(nStartId+0);
		lstInds.Add(nStartId+i+1);
		lstInds.Add(nStartId+i+2);
	}

	return false;
}

float CRoadRenderNode::GetMaxViewDist()
{
	if (GetMinSpecFromRenderNodeFlags(m_dwRndFlags) == CONFIG_DETAIL_SPEC)
		return max(GetCVars()->e_ViewDistMin,GetBBox().GetRadius()*GetCVars()->e_ViewDistRatioDetail*GetViewDistRatioNormilized());

	return max(GetCVars()->e_ViewDistMin,GetBBox().GetRadius()*GetCVars()->e_ViewDistRatio*GetViewDistRatioNormilized());
}

void CRoadRenderNode::SetMaterial(IMaterial * pMat) 
{ 
	m_pMaterial = pMat; 
}

void CRoadRenderNode::DePhysicalize()
{
#if ROAD_PHYSICS 
  if(m_pPhysEnt)
  {
    GetPhysicalWorld()->GetGeomManager()->UnregisterGeometry(m_pPhysGeom);
    m_pPhysEnt->RemoveGeometry(0);
    GetPhysicalWorld()->DestroyPhysicalEntity(m_pPhysEnt);
    m_pPhysEnt = NULL;
  }
#endif
}

void CRoadRenderNode::Physicalize(uint16 *pIndices,int nIndices, Vec3 *pVerts, int nPhysMatId)
{
#if ROAD_PHYSICS 

  FUNCTION_PROFILER_3DENGINE;

  int flags = mesh_multicontact1 | mesh_SingleBB | mesh_no_vtx_merge | mesh_always_static | mesh_approx_box;

  int arrSurfaceTypesId[16];
  memset(arrSurfaceTypesId,0,sizeof(arrSurfaceTypesId));

  IGeomManager *pGeoman = GetPhysicalWorld()->GetGeomManager();
  IGeometry * pGeom = pGeoman->CreateMesh(pVerts, pIndices, NULL, NULL, nIndices/3, flags, 1e10f);
  m_pPhysGeom = pGeoman->RegisterGeometry(pGeom,nPhysMatId);
  pGeom->Release();

  assert(!m_pPhysEnt);
  m_pPhysEnt = GetPhysicalWorld()->CreatePhysicalEntity(PE_STATIC,NULL,NULL,PHYS_FOREIGN_ID_TERRAIN);

  pe_action_remove_all_parts remove_all;
  m_pPhysEnt->Action(&remove_all);

  pe_geomparams params;	  
	params.flags = geom_mat_substitutor;
  m_pPhysEnt->AddGeometry(m_pPhysGeom, &params);

  pe_params_flags par_flags;
  par_flags.flagsOR = pef_never_affect_triggers;
  m_pPhysEnt->SetParams(&par_flags);

  pe_params_pos par_pos;
  Matrix34 mat; mat.SetIdentity();
  mat.SetTranslation(Vec3(0,0,0.01f));
  par_pos.pMtx3x4 = &mat;
  m_pPhysEnt->SetParams(&par_pos);

  pe_params_foreign_data par_foreign_data;
  par_foreign_data.pForeignData = (IRenderNode*)this;
  par_foreign_data.iForeignData = PHYS_FOREIGN_ID_STATIC;
  m_pPhysEnt->SetParams(&par_foreign_data);

#endif
}

void CRoadRenderNode::GetMemoryUsage(ICrySizer * pSizer) const
{
	SIZER_COMPONENT_NAME(pSizer, "Road");
	pSizer->AddObject(this, sizeof(*this));
	pSizer->AddObject(m_arrVerts);
}

void CRoadRenderNode::ScheduleRebuild()
{
  if(Get3DEngine()->m_lstRoadRenderNodesForUpdate.Find(this)<0)
    Get3DEngine()->m_lstRoadRenderNodesForUpdate.Add(this);
}

void CRoadRenderNode::OnTerrainChanged()
{
  if(!m_pRenderMesh)
    return;

  int nPosStride = 0;

	IRenderMesh::ThreadAccessLock lock(m_pRenderMesh); 

  byte * pPos = m_pRenderMesh->GetPosPtr(nPosStride, FSL_SYSTEM_UPDATE);

  Vec3 vWSBoxCenter = m_WSBBox.GetCenter(); //vWSBoxCenter.z=0;

  for(int i=0, nVertsNum = m_pRenderMesh->GetVerticesCount(); i<nVertsNum; i++)
  {
    Vec3 & vPos = *(Vec3*)&pPos[i*nPosStride];
    vPos.z = GetTerrain()->GetZApr(vWSBoxCenter.x+vPos.x,vWSBoxCenter.y+vPos.y,false,/*m_nSID*/GetDefSID())+0.01f - vWSBoxCenter.z;
  }
  m_pRenderMesh->UnlockStream(VSF_GENERAL);
  ScheduleRebuild();
}

void CRoadRenderNode::OnRenderNodeBecomeVisible()
{
  assert(m_pRNTmpData);
  m_pRNTmpData->userData.objMat.SetIdentity();
}

void CRoadRenderNode::GetTexCoordInfo(float * pTexCoordInfo)
{
  pTexCoordInfo[0] = m_arrTexCoors[0];
  pTexCoordInfo[1] = m_arrTexCoors[1];
  pTexCoordInfo[2] = m_arrTexCoorsGlobal[0];
  pTexCoordInfo[3] = m_arrTexCoorsGlobal[1];
}

void CRoadRenderNode::GetClipPlanes(Plane * pPlanes, int nPlanesNum, int nVertId)
{ 
  const Vec3 * pVerts = &m_arrVerts[nVertId];

  if(	
    pVerts[0] == pVerts[1] ||
    pVerts[1] == pVerts[2] ||
    pVerts[2] == pVerts[3] ||
    pVerts[3] == pVerts[0])
    return;

  assert(nPlanesNum == 4 || nPlanesNum == 6);

  // define 6 clip planes
  pPlanes[0].SetPlane(pVerts[0],pVerts[1],pVerts[1]+Vec3(0,0,1));
  pPlanes[1].SetPlane(pVerts[2],pVerts[3],pVerts[3]+Vec3(0,0,-1));
  pPlanes[2].SetPlane(pVerts[0],pVerts[2],pVerts[2]+Vec3(0,0,-1));
  pPlanes[3].SetPlane(pVerts[1],pVerts[3],pVerts[3]+Vec3(0,0,1));

  if(nPlanesNum==6)
  {
    Vec3 vHeight(0,0,fRoadAreaZRange);
    pPlanes[4].SetPlane(pVerts[0]-vHeight,pVerts[1]-vHeight,pVerts[2]-vHeight);
    pPlanes[5].SetPlane(pVerts[1]+vHeight,pVerts[0]+vHeight,pVerts[2]+vHeight);
  }
}

void CRoadRenderNode::OffsetPosition(const Vec3& delta)
{
#ifdef SEG_WORLD
	if (m_pRNTmpData) m_pRNTmpData->OffsetPosition(delta);
	m_WSBBox.Move(delta);
#endif
}
